import { MidiItem } from "@/lib/midi";
import { FopApi, PlaylistApi } from "@/lib/api";
import { DefaultMapper } from "@/lib/constants";
import { PlayMode, PlayState, PlaylistPlayer, type PlaylistObject, type PlaylistObjectKey } from "./types";

/**
 * Playlist 命名空间，内部均为对 PlaylistObject 的处理函数
 */
export class Playlist {
    /**
     * 需要进行存储的键值
     */
    private static _storageKeys: PlaylistObjectKey[] = ["items", "indexes", "focus", "shuffle", "mode"];

    /**
     * 生成一个新的 PlaylistPlayer
     * @returns 新的 PlaylistPlayer
     */
    public static player(): PlaylistPlayer {
        return {
            id: "",
            name: "",
            state: PlayState.Stop,
            start: new Date(NaN),
            point: 0,
            duration: 0,
        };
    }

    /**
     * 生成一个新的 PlaylistObject
     * @returns 新的 PlaylistObject
     */
    public static object(): PlaylistObject {
        return {
            items: [],
            indexes: [],
            focus: -1,
            shuffle: false,
            mode: PlayMode.Order,
            mapper: DefaultMapper,
            player: Playlist.player(),
        };
    }

    /**
     * 对 PlaylistObject 进行数据校验
     * @param playlistObject 待校验的 PlaylistObject
     * @returns 校验后的 PlaylistObject
     */
    public static check(playlistObject: PlaylistObject): PlaylistObject {
        // 1. items
        if (!(playlistObject.items instanceof Array)) {
            playlistObject.items = [];
        } else {
            for (let i = 0; i < playlistObject.items.length; i++) {
                const item = playlistObject.items[i];
                if (typeof item !== "object" || !("url" in item) || typeof item.url !== "string") {
                    playlistObject.items.splice(i, 1);
                    i--;
                }
            }
        }

        // 2. indexes
        if (
            !(playlistObject.indexes instanceof Array) ||
            playlistObject.indexes.length !== playlistObject.items.length
        ) {
            playlistObject.indexes = Array.from({ length: playlistObject.items.length }, (_, index) => index);
            playlistObject.shuffle = false;
        } else {
            const indexesSet = new Set<number>();
            for (let i = 0; i < playlistObject.indexes.length; i++) {
                const index = playlistObject.indexes[i];
                if (typeof index === "number" && index >= 0 && index < playlistObject.items.length) {
                    indexesSet.add(index);
                }
            }
            if (indexesSet.size !== playlistObject.indexes.length) {
                playlistObject.indexes = Array.from({ length: playlistObject.items.length }, (_, index) => index);
                playlistObject.shuffle = false;
            }
        }

        // 3. focus
        if (
            typeof playlistObject.focus !== "number" ||
            playlistObject.focus < -1 ||
            playlistObject.focus >= playlistObject.items.length
        ) {
            playlistObject.focus = -1;
        }

        // 4. shuffle
        if (typeof playlistObject.shuffle !== "boolean") {
            playlistObject.shuffle = false;
            playlistObject.indexes = Array.from({ length: playlistObject.items.length }, (_, index) => index);
        }

        // 5. mode
        if (
            typeof playlistObject.mode !== "number" ||
            playlistObject.mode < 0 ||
            playlistObject.mode >= Object.entries(PlayMode).length
        ) {
            playlistObject.mode = PlayMode.Order;
        }

        return playlistObject;
    }

    /**
     * 从 playlist.json 中加载 PlaylistObject。
     * 注意：不会按 focus 设置 player
     * @param loadKeys 加载的键值（可选）
     * @returns 加载的 PlaylistObject
     */
    public static async load(...loadKeys: PlaylistObjectKey[]): Promise<PlaylistObject> {
        // 默认需要读取的键值
        if (loadKeys === undefined || loadKeys.length === 0) {
            loadKeys = Playlist._storageKeys;
        }

        // 加载数据
        let data: { [key: string]: any } = {};
        await Promise.all(
            loadKeys.map(async (key) => {
                try {
                    data[key] = await FopApi.readJson(`playlist/${key}`);
                } catch (error) {
                    console.error("播放列表数据", key, "读取失败", error);
                }
            }),
        );

        // 只保留部分数据
        const tmpData = data;
        data = {};
        for (const key of loadKeys) {
            if (tmpData[key] !== undefined) {
                data[key] = tmpData[key];
            }
        }

        // 构造 PlaylistObject
        const playlistObject = {
            ...Playlist.object(),
            ...data,
        };

        // 数据校验
        return Playlist.check(playlistObject);
    }

    /**
     * 保存 PlaylistObject 到 playlist.json
     * @param playlistObject 要进行保存的 PlaylistObject
     * @param saveKeys 保存的键值（可选）
     */
    public static async save(playlistObject: PlaylistObject, ...saveKeys: PlaylistObjectKey[]): Promise<void> {
        // 默认需要保存的键值
        if (saveKeys === undefined || saveKeys.length === 0) {
            saveKeys = Playlist._storageKeys;
        }

        // 只保留部分数据
        const data = Playlist.extract(playlistObject, ...saveKeys);

        // 保存数据
        await Promise.all(
            saveKeys.map(async (key) => {
                try {
                    await FopApi.writeJson(`playlist/${key}`, data[key]);
                } catch (error) {
                    console.error("播放列表数据", key, "保存失败", error);
                }
            }),
        );
    }

    /**
     * 提取 PlaylistObject 中的需要进行保存的数据
     * @param playlistObject 要进行保存的 PlaylistObject
     * @param saveKeys 保存的键值（可选）
     * @returns 需要进行保存的数据
     */
    public static extract(playlistObject: PlaylistObject, ...saveKeys: PlaylistObjectKey[]): { [key: string]: any } {
        // 默认需要保存的键值
        if (saveKeys === undefined || saveKeys.length === 0) {
            saveKeys = Playlist._storageKeys;
        }

        // 加载数据
        const data: { [key: string]: any } = {};

        // 只保留部分数据
        for (const key of Playlist._storageKeys) {
            if (playlistObject[key] !== undefined) {
                data[key] = playlistObject[key];
            }
        }

        return data;
    }

    /**
     * 获取上一个焦点索引
     * @param playlistObject 要处理的播放列表对象
     * @returns 上一个焦点索引，如果不存在则返回 -1
     */
    public static previousFocus(playlistObject: PlaylistObject): number {
        if (playlistObject.items.length === 0) {
            return -1;
        }

        switch (playlistObject.mode) {
            case PlayMode.Once:
                return -1;
            case PlayMode.Single:
                return playlistObject.focus;
            case PlayMode.Order:
                return playlistObject.focus - 1 < 0 ? -1 : playlistObject.focus - 1;
            case PlayMode.Loop:
                return (playlistObject.focus - 1 + playlistObject.items.length) % playlistObject.items.length;
        }
    }

    /**
     * 获取下一个焦点索引
     * @param playlistObject 要处理的播放列表对象
     * @returns 下一个焦点索引，如果为循环播放则返回 -1
     */
    public static nextFocus(playlistObject: PlaylistObject): number {
        if (playlistObject.items.length === 0) {
            return -1;
        }

        switch (playlistObject.mode) {
            case PlayMode.Once:
                return -1;
            case PlayMode.Single:
                return playlistObject.focus;
            case PlayMode.Order:
                return playlistObject.focus + 1 >= playlistObject.items.length ? -1 : playlistObject.focus + 1;
            case PlayMode.Loop:
                return (playlistObject.focus + 1) % playlistObject.items.length;
        }
    }

    /**
     * 插入新的 Midi 项
     * @param playlistObject 要进行插入的播放列表对象
     * @param item 新 MidiItem
     * @param index 插入索引，默认为最后
     */
    public static insert(playlistObject: PlaylistObject, item: MidiItem, index?: number) {
        // 处理插入索引（items）
        let realIndex = index ?? playlistObject.items.length;
        realIndex =
            realIndex < 0 ? 0 : realIndex > playlistObject.items.length ? playlistObject.items.length : realIndex;

        // 新索引（indexes）
        const newIndex = playlistObject.items.length;

        // 处理 focus
        if (playlistObject.focus !== -1 && playlistObject.focus >= realIndex) {
            playlistObject.focus += 1;
        }

        if (playlistObject.shuffle) {
            // 处于随机列表状态，只处理索引
            playlistObject.items.push(item);
            if (realIndex === 0) {
                playlistObject.indexes.unshift(newIndex);
            } else if (realIndex === playlistObject.items.length) {
                playlistObject.indexes.push(newIndex);
            } else {
                playlistObject.indexes.splice(realIndex, 0, newIndex);
            }
        } else {
            // 正常状态，处理列表本身
            playlistObject.indexes.push(newIndex);
            if (realIndex === 0) {
                playlistObject.items.unshift(item);
            } else if (realIndex === playlistObject.items.length) {
                playlistObject.items.push(item);
            } else {
                playlistObject.items.splice(realIndex, 0, item);
            }
        }
    }

    /**
     * 移除 Midi 项
     * @param playlistObject 要进行移除的播放列表对象
     * @param indexes 移除索引，默认为最后
     */
    public static remove(playlistObject: PlaylistObject, ...indexes: number[]) {
        // 用于处理 indexes
        let indexOffset: number[] = new Array(playlistObject.indexes.length).fill(0);

        // 用于处理 Midi 项
        let itemDelete: number[] = [];

        const removeOne = (index: number) => {
            // 处理删除索引
            if (index < 0 || index >= playlistObject.indexes.length) {
                return;
            }

            // 处理 focus
            if (playlistObject.focus !== -1) {
                if (playlistObject.focus > index) {
                    playlistObject.focus -= 1;
                } else if (playlistObject.focus === index) {
                    playlistObject.focus = -1;
                }
            }

            // 删除对应索引
            if (playlistObject.shuffle) {
                if (index === 0) {
                    index = playlistObject.indexes.shift()!;
                } else if (index === playlistObject.indexes.length - 1) {
                    index = playlistObject.indexes.pop()!;
                } else {
                    index = playlistObject.indexes.splice(index, 1)[0]!;
                }
            } else {
                playlistObject.indexes.pop();
            }

            // 标注，大于这个 index 的相应减一
            indexOffset[index] = 1;

            // 标注要删除的 Midi 项
            itemDelete.push(index);
        };

        const removeItem = (index: number) => {
            // 删除 Midi 项
            if (index === 0) {
                playlistObject.items.shift();
            } else if (index === playlistObject.items.length - 1) {
                playlistObject.items.pop();
            } else {
                playlistObject.items.splice(index, 1);
            }
        };

        // 删除索引
        if (indexes.length === 0) {
            removeOne(playlistObject.indexes.length - 1);
        } else {
            indexes.sort((a, b) => b - a);
            for (let index of indexes) {
                removeOne(index);
            }
        }

        // 删除 Midi 项
        itemDelete.sort((a, b) => b - a);
        for (let index of itemDelete) {
            removeItem(index);
        }

        // 处理 indexes
        for (let i = 1; i < indexOffset.length; ++i) {
            indexOffset[i] += indexOffset[i - 1];
        }
        for (let i = 0; i < playlistObject.indexes.length; ++i) {
            playlistObject.indexes[i] -= indexOffset[playlistObject.indexes[i]];
        }
    }

    /**
     * 移除所有 Midi 项
     * @param playlistObject 要进行移除的播放列表对象
     */
    public static removeAll(playlistObject: PlaylistObject) {
        playlistObject.focus = -1;
        playlistObject.indexes.length = 0;
        playlistObject.items.length = 0;
    }

    /**
     * 交换两个 MidiItem 的位置
     * @param playlistObject 要进行交换的播放列表对象
     * @param index1 Midi 项1 的索引
     * @param index2 Midi 项2 的索引
     * @returns 是否交换成功
     */
    public static swap(playlistObject: PlaylistObject, index1: number, index2: number): boolean {
        // 检查索引是否合法
        if (
            index1 < 0 ||
            index1 >= playlistObject.items.length ||
            index2 < 0 ||
            index2 >= playlistObject.items.length
        ) {
            return false;
        }

        // 进行交换
        Playlist.swapUnchecked(playlistObject, index1, index2);
        return true;
    }

    /**
     * 交换两个 MidiItem 的位置（不进行检查）
     * @param playlistObject 要进行交换的播放列表对象
     * @param index1 Midi 项1 的索引
     * @param index2 Midi 项2 的索引
     * @param toShuffle 将要打乱顺序，默认为 false
     */
    private static swapUnchecked(
        playlistObject: PlaylistObject,
        index1: number,
        index2: number,
        toShuffle: boolean = false,
    ) {
        // 相同索引
        if (index1 === index2) {
            return;
        }

        // 交换 Midi 项的位置
        if (playlistObject.shuffle || toShuffle) {
            [playlistObject.indexes[index1], playlistObject.indexes[index2]] = [
                playlistObject.indexes[index2],
                playlistObject.indexes[index1],
            ];
        } else {
            [playlistObject.items[index1], playlistObject.items[index2]] = [
                playlistObject.items[index2],
                playlistObject.items[index1],
            ];
        }

        // 处理 focus
        if (playlistObject.focus === index1) {
            playlistObject.focus = index2;
        } else if (playlistObject.focus === index2) {
            playlistObject.focus = index1;
        }
    }

    /**
     * 切换 Midi 项列表的随机状态
     * @param playlistObject 要进行切换的播放列表对象
     */
    public static toggleShuffle(playlistObject: PlaylistObject) {
        const length = playlistObject.indexes.length;
        if (playlistObject.shuffle) {
            // 恢复原有顺序
            if (playlistObject.focus !== -1) {
                playlistObject.focus = playlistObject.indexes[playlistObject.focus];
            }
            for (let index = 0; index < length; ++index) {
                playlistObject.indexes[index] = index;
            }
        } else {
            // 打乱播放列表
            for (let index = 0; index < length; ++index) {
                Playlist.swapUnchecked(
                    playlistObject,
                    index,
                    index + Math.floor(Math.random() * (length - index)),
                    true,
                );
            }
        }

        // 状态切换
        playlistObject.shuffle = !playlistObject.shuffle;
    }

    /**
     * 用于管理播放行为
     */
    public static Player = {
        /**
         * 是否正在播放
         * @param playlistObject 播放列表对象
         * @returns 是否正在播放
         */
        isPlaying(playlistObject: PlaylistObject): boolean {
            return playlistObject.player.id !== "" && playlistObject.player.state === PlayState.Play;
        },

        /**
         * 重置播放器
         * @param playlistObject 播放列表对象
         */
        resetPlayer(playlistObject: PlaylistObject) {
            playlistObject.player = Playlist.player();

            // 此时 focus 不为 -1，则设置 player 信息
            if (playlistObject.focus !== -1) {
                const item = playlistObject.items[playlistObject.indexes[playlistObject.focus]];
                playlistObject.player.name = item.name ?? "";
                playlistObject.player.duration = item.duration ?? 0;
            }
        },

        /**
         * 开始或继续播放
         * @param playlistObject 播放列表对象
         */
        async play(playlistObject: PlaylistObject) {
            // 如果没有打开 Midi 文件
            if (playlistObject.player.id === "") {
                // 有无选中项
                if (playlistObject.focus === -1) {
                    console.warn("没有选中任何 Midi 项");
                    return;
                }

                // 获取 midi 信息
                const midi = playlistObject.items[playlistObject.indexes[playlistObject.focus]];

                // 打开 midi 文件
                let id: string;
                try {
                    id = await PlaylistApi.open(playlistObject.mapper, midi.url);
                } catch (error) {
                    console.error("打开文件", midi.url, "失败", error);
                    return;
                }

                // 设置播放信息
                Playlist.Player.resetPlayer(playlistObject);
                playlistObject.player.id = id;
            }

            // 开始播放
            try {
                await PlaylistApi.play(playlistObject.player.id, playlistObject.player.point);
            } catch (error) {
                console.error("播放失败", error);
                return;
            }
            playlistObject.player.start = new Date(new Date().getTime() - playlistObject.player.point);
            playlistObject.player.state = PlayState.Play;
        },

        /**
         * 重新播放
         * @param playlistObject 播放列表对象
         */
        async replay(playlistObject: PlaylistObject) {
            if (playlistObject.player.id === "") {
                // 没有打开 Midi 文件
                // 直接播放
                await Playlist.Player.play(playlistObject);
            } else {
                // 停止播放
                await Playlist.Player.stop(playlistObject);
                // 重新播放
                await Playlist.Player.play(playlistObject);
            }
        },

        /**
         * 暂停播放
         * @param playlistObject 播放列表对象
         */
        async pause(playlistObject: PlaylistObject) {
            if (playlistObject.player.id === "") {
                Playlist.Player.resetPlayer(playlistObject);
                return;
            }

            if (playlistObject.player.state === PlayState.Play) {
                playlistObject.player.point = new Date().getTime() - playlistObject.player.start.getTime();
                playlistObject.player.state = PlayState.Stop;
                await PlaylistApi.stop(playlistObject.player.id);
            }
        },

        /**
         * 停止播放
         * @param playlistObject 播放列表对象
         */
        async stop(playlistObject: PlaylistObject) {
            if (playlistObject.player.id === "") {
                Playlist.Player.resetPlayer(playlistObject);
                return;
            }

            playlistObject.player.point = 0;
            playlistObject.player.state = PlayState.Stop;
            await PlaylistApi.stop(playlistObject.player.id);
        },

        /**
         * 关闭播放
         * @param playlistObject 播放列表对象
         */
        async close(playlistObject: PlaylistObject) {
            if (playlistObject.player.id !== "") {
                const id = playlistObject.player.id;
                Playlist.Player.resetPlayer(playlistObject);
                await PlaylistApi.close(id);
            } else {
                Playlist.Player.resetPlayer(playlistObject);
            }
        },

        /**
         * 进度跳转（前提是未关闭）
         * @param playlistObject 播放列表对象
         * @param target 目标进度，介于 0 到 1 之间
         */
        async jump(playlistObject: PlaylistObject, target: number) {
            if (playlistObject.player.id === "" || target < 0 || target > 1) {
                return;
            }

            playlistObject.player.point = Math.floor(playlistObject.player.duration * target);

            // 停止正在播放的
            if (playlistObject.player.state === PlayState.Play) {
                playlistObject.player.state = PlayState.Stop;
                await PlaylistApi.stop(playlistObject.player.id);
            }

            // 进度跳转
            await PlaylistApi.play(playlistObject.player.id, playlistObject.player.point);
            playlistObject.player.start = new Date(new Date().getTime() - playlistObject.player.point);
            playlistObject.player.state = PlayState.Play;
        },
    };
}
